import './index.css'
import * as THREE from 'three'
import {
  OrbitControls
} from 'three/examples/jsm/controls/OrbitControls'
import gasp from 'gsap'

/**
 * Size
 */
const Size = {
  width: window.innerWidth,
  height: window.innerHeight
}


/**
 * Camera
 */
const camera = new THREE.PerspectiveCamera(75, Size.width / Size.height, 0.1, 100)
camera.position.z = 3

/**
 * Scene
 */
const scene = new THREE.Scene()


/**
 * Load Img
 */
const paths = [
  "./assets/svg/fish.svg",
  "./assets/svg/circle.svg",
  "./assets/svg/fire.svg",
  "./assets/svg/triangle.svg"
];

function loadImages(paths, whenLoaded) {
  const images = [];
  paths.forEach((path) => {
    const img = new Image();
    img.onload = function () {
      images.push(img);
      if (images.length === paths.length) {
        whenLoaded(images);
      }
    };
    img.src = path;
  });
}




const size = 100;
const canvas = document.createElement("canvas");
canvas.width = size;
canvas.height = size;
const ctx = canvas.getContext("2d");

function getImageCoords(img) {
  ctx.clearRect(0, 0, size, size);
  ctx.drawImage(img, 0, 0, size, size);
  const data = ctx.getImageData(0, 0, size, size).data;
  const imageCoords = [];
  for (let y = 0; y < size; y++) {
    for (let x = 0; x < size; x++) {
      const red = data[(y * size + x) * 4];
      // const green = data[(y * size + x) * 4 + 1];
      // const blue = data[(y * size + x) * 4 + 2];
      // const alpha = data[(y * size + x) * 4 + 3];
      if (red > 0 && red < 50) {
        imageCoords.push([x / size - 0.5, 0.5 - y / size]);
      }
    }
  }

  return imageCoords;
}



/**
 * Shader
 */

const vertexShader = /* GLSL */ `
  varying vec2 vUv;
  varying float vDepth;
  uniform float uTime;
  attribute float aOffset;
  attribute vec3 position1;
  // varying float vProgress;
  uniform float uProgress;
  void main() {
    float PI = 3.1415926;
    vDepth = position.z;
    vUv = uv;
    // vProgress = abs(sin(uTime));
    vec3 newPos =  mix(position,position1,uProgress);
    newPos.z += 0.03 * sin(aOffset * PI * 2.0 + uTime * 5.0);
    gl_Position = projectionMatrix * modelViewMatrix * vec4(newPos, 1.0);
    gl_PointSize = 12.0;
  }
`;

const fragmentShader = /* GLSL */ `
  varying vec2 vUv;
  uniform vec3 uColor;
  uniform vec3 uColor1;
  // varying float vProgress;
  uniform float uProgress;
  varying float vDepth;
  

  vec3 rgb2hsl(vec3 color) {
    vec3 hsl; // init to 0 to avoid warnings ? (and reverse if + remove first part)

    float fmin = min(min(color.r, color.g), color.b); //Min. value of RGB
    float fmax = max(max(color.r, color.g), color.b); //Max. value of RGB
    float delta = fmax - fmin; //Delta RGB value

    hsl.z = (fmax + fmin) / 2.0; // Luminance

    if (delta == 0.0) //This is a gray, no chroma...
    {
        hsl.x = 0.0; // Hue
        hsl.y = 0.0; // Saturation
    } else //Chromatic data...
    {
        if (hsl.z < 0.5)
            hsl.y = delta / (fmax + fmin); // Saturation
        else
            hsl.y = delta / (2.0 - fmax - fmin); // Saturation

        float deltaR = (((fmax - color.r) / 6.0) + (delta / 2.0)) / delta;
        float deltaG = (((fmax - color.g) / 6.0) + (delta / 2.0)) / delta;
        float deltaB = (((fmax - color.b) / 6.0) + (delta / 2.0)) / delta;

        if (color.r == fmax)
            hsl.x = deltaB - deltaG; // Hue
        else if (color.g == fmax)
            hsl.x = (1.0 / 3.0) + deltaR - deltaB; // Hue
        else if (color.b == fmax)
            hsl.x = (2.0 / 3.0) + deltaG - deltaR; // Hue

        if (hsl.x < 0.0)
            hsl.x += 1.0; // Hue
        else if (hsl.x > 1.0)
            hsl.x -= 1.0; // Hue
    }

    return hsl;
    }

    // https://github.com/Experience-Monks/glsl-hsl2rgb/blob/master/index.glsl
    float hue2rgb(float f1, float f2, float hue) {
        if (hue < 0.0)
            hue += 1.0;
        else if (hue > 1.0)
            hue -= 1.0;
        float res;
        if ((6.0 * hue) < 1.0)
            res = f1 + (f2 - f1) * 6.0 * hue;
        else if ((2.0 * hue) < 1.0)
            res = f2;
        else if ((3.0 * hue) < 2.0)
            res = f1 + (f2 - f1) * ((2.0 / 3.0) - hue) * 6.0;
        else
            res = f1;
        return res;
    }

    vec3 hsl2rgb(vec3 hsl) {
        vec3 rgb;
        
        if (hsl.y == 0.0) {
            rgb = vec3(hsl.z); // Luminance
        } else {
            float f2;
            
            if (hsl.z < 0.5)
                f2 = hsl.z * (1.0 + hsl.y);
            else
                f2 = hsl.z + hsl.y - hsl.y * hsl.z;
                
            float f1 = 2.0 * hsl.z - f2;
            
            rgb.r = hue2rgb(f1, f2, hsl.x + (1.0/3.0));
            rgb.g = hue2rgb(f1, f2, hsl.x);
            rgb.b = hue2rgb(f1, f2, hsl.x - (1.0/3.0));
        }   
        return rgb;
    }

    vec3 hsl2rgb(float h, float s, float l) {
        return hsl2rgb(vec3(h, s, l));
    }


  void main() {
    // vec3 color = uColor;
    vec3 hsl = rgb2hsl(uColor);
    hsl.b  += vDepth * 1.0;
    vec3 color = hsl2rgb(hsl);

    vec3 hsl1 = rgb2hsl(uColor1);
    hsl1.b  += vDepth * 1.0;
    vec3 color1 = hsl2rgb(hsl1);

    vec3 newColor = mix(color, color1, uProgress);

    float dist = distance(gl_PointCoord, vec2(0.5));
    float mask = smoothstep(0.5, 0.499, dist);
    // float mask = step(dist, 0.5);
    gl_FragColor = vec4(newColor, 1.0 * mask);
  }
`;


function getRandomValue(data) {
  return data[Math.floor(Math.random() * data.length)];
}


function setGeometryAttributes(imageCoords) {
  const positions = new Float32Array(count * 3);
  for (let i = 0; i < count * 3; i += 3) {
    const [x, y] = getRandomValue(imageCoords);
    const scale = 3;
    const z = Math.random();
    positions.set([x * scale, y * scale, z * -0.3], i);
  }
  return positions;
}

const COLORS = [
  "rgb(244,67,54)",
  "rgb(233,30,99)",
  "rgb(156,39,176)",
  "rgb(103,58,183)",
  "rgb(63,81,181)",
  "rgb(33,150,243)",
  "rgb(3,169,244)",
  "rgb(0,188,212)",
  "rgb(0,150,136)",
  "rgb(76,175,80)",
  "rgb(139,195,74)",
  "rgb(205,220,57)",
  "rgb(255,235,59)",
  "rgb(255,193,7)",
  "rgb(255,152,0)",
  "rgb(255,87,34)",
];


let current = 0;
let geometryAttributes = [];
const count = size * size;
const offsets = new Float32Array(count);
let geometry, material, points;

loadImages(paths, function (images) {
  // 解析坐标
  images.forEach(img => {
    const imageCoords = getImageCoords(img);
    geometryAttributes.push(setGeometryAttributes(imageCoords))
  })

  geometry = new THREE.BufferGeometry();

  for (let i = 0; i < count; i++) {
    offsets.set([Math.random()], i);
  }
  geometry.setAttribute(
    "position",
    new THREE.BufferAttribute(geometryAttributes[current], 3)
  );
  geometry.setAttribute(
    "position1",
    new THREE.BufferAttribute(geometryAttributes[current], 3)
  );

  geometry.setAttribute("aOffset", new THREE.BufferAttribute(offsets, 1));

  const color = new THREE.Color(getRandomValue(COLORS));
  material = new THREE.ShaderMaterial({
    uniforms: {
      uTime: {
        value: 0
      },
      uProgress: {
        value: 0
      },
      uColor: {
        value: color
      },
      uColor1: {
        value: color
      },
    },
    vertexShader,
    fragmentShader,
    transparent: true,
    depthTest: false,
    depthWrite: false,
  });

  points = new THREE.Points(geometry, material);
  scene.add(points);
})


/**
 * Renderer
 */
const canvasMain = document.querySelector('canvas.webgl')
const renderer = new THREE.WebGLRenderer({
  canvas: canvasMain
})
renderer.setSize(Size.width, Size.height)
renderer.setClearColor('#f19191')


/**
 * OrbitControls
 */
const controls = new OrbitControls(camera, canvasMain)
controls.enableDamping = true

/**
 * Click
 */
let animating = false;
window.addEventListener('click', () => {
  if (!animating) {
    animating = true
    current++;
    current = current % paths.length;

    // 将要变的图形坐标
    geometry.attributes.position1.array = geometryAttributes[current];
    geometry.attributes.position1.needsUpdate = true
    const color = new THREE.Color(getRandomValue(COLORS));
    material.uniforms.uColor1.value = color

    gasp.to(material.uniforms.uProgress, {
      value: 1,
      // 完成时钩子函数 
      onComplete: () => {
        animating = false
        //变化完成，讲mix过渡的初始图形也变为变化后图形坐标
        geometry.attributes.position.array = geometryAttributes[current];
        geometry.attributes.position.needsUpdate = true
        material.uniforms.uColor.value = color
        material.uniforms.uProgress.value = 0
      }
    })
  }
})


/**
 * Update
 */
const clock = new THREE.Clock();
let elapsedTime = 0;
const tick = () => {
  controls.update();
  elapsedTime = clock.getElapsedTime();
  if (material) {
    material.uniforms.uTime.value = elapsedTime;
  }
  renderer.render(scene, camera)
  requestAnimationFrame(tick)
}
tick()